/**
 * 1
 * 定义一个函数
 * 检测数据的类型
 * 常用的为typeof typeof()
 * 但是它无法检测出Array和Object，因为这两者它返回的都是Object
 * 使用tostring会更合适
 * toString()方法返回反映这个对象的字符串
 * 常常使用Object.prototype.toString()来进行类型识别，返回代表该对象的[object 数据类型]字符串表示
 * /\[object (\w+)\]/: 这是一个正则表达式，匹配形如 [object <类型>] 的字符串
 * \[object 匹配字符串中的 [object
 * (\w+) 匹配一个或多个连续的字母、数字或下划线，并使用括号将其捕获为分组
 * '$1': 这是替换字符串的模式，$1 表示替换为第一个分组（即正则表达式中的 (\w+) 所匹配到的内容）
 * 这段代码的作用是将形如 [object <类型>] 的字符串，提取出其中的 <类型> 部分，并进行替换
 */
const isTypeOf = (data) => {
  return Object.prototype.toString
    .call(data)
    .replace(/\[object (\w+)\]/, "$1")
    .toLowerCase();
};

/**
 * 2
 * 有时我们会碰到后端给的数据不符合我们的格式
 * 比如给了这么一种数据
 * @param { 0: 'a', 1: 'b', 2: 'c'}
 * 我们需要的是
 * @return [{key:0,value:1},{key:2,value:b},{key:2,value:c}]
 * 这时就需要我们自己处理一下数据
 * 这个函数就是进行这种数据转化用的
 * name1和name2是自己自定义的属性名，后面可以自己随意更改
 * 这里有结构的name和content，这两个也是可以随意改的，只是一个代号
 */
function changeTypeToArray(obj, name1, name2) {
  let final = [];
  for (const [name, content] of Object.entries(obj)) {
    let objs = {};
    objs[name1] = name;
    objs[name2] = content;
    final.push(objs);
  }
  return final;
}

/**
 * 3
 * 这里还有一种对应的格式，就是给出的是一个数组套对象
 * 但是不符合我们的需求，我们想要根据对应的值，直接拿到数据
 * @param [{id: 1,value: '正式'},{id: 2,value: '非正式'}] object
 * @returns { '1': '正式', '2': '非正式' }
 * 这个和刚才的那个刚好反过来，这就需要我们自己处理一下
 * 我们要给这些值一个意义对应的数据关系，然后可以快速的找到这个值
 * 注意这里必须是id和value，如果是其他的，需要改一下属性名
 */
const changeTypeToObject = (arr) => {
  return arr.reduce((acc, item) => {
    return { ...acc, [item.id]: item.value };
  }, {});
};

/**
 * 4.
 * 给后端传递的数据，如果有些form表单中不为空，可以传没问题，如果有
 * 空的情况下，一般我们会把这个空给去掉，过过滤掉，
 * 这里这个函数的作用就是过滤掉不可用的属性
 * @param form{可以有空值}
 * @returns form{取消没有值部分}
 */
function cleanObjectNullValue(object) {
  const isVoid = (value) =>
    value === undefined || value === null || value === "";
  if (!object) {
    return;
  }
  const result = { ...object };
  Object.keys(result).forEach((key) => {
    const value = result[key];
    if (isVoid(value)) {
      delete result[key];
    }
  });
  return result;
}

/**
 * 5
 * 新建一个处理树形结构的函数
 * 入参的关键之为id和pid
 * 最顶级的参数id为''，这个是判断最顶级的表示
 * 如果数据中最顶级为其他，可以修改其他参数，或者修改
 * 自己的代码，关键的参数一定要对应上
 * 输出的关系为child
 * 这个也可以考虑使用reduce方法，进行两次过滤，都可以实现
 */
function setTree(list) {
  let cloneList = JSON.parse(JSON.stringify(list));
  return cloneList.filter((father) => {
    // 这个过滤出的是
    let branchArr = cloneList.filter((son) => {
      return son.pid === father.id;
    });
    branchArr.length > 0 ? (father.child = branchArr) : "";
    // 这里是判定最顶级的父元素的id,如果是0 ，就写0，如果是 '' ,就写空
    return father.pid === 0;
  });
}

// 这种方法不用考虑顶级父元素的id是哪一个
function setTree2(data) {
  const treeMapList = data.reduce((memo, current) => {
    memo[current.id] = current;
    return memo;
  }, {});
  // console.log(treeMapList)
  const mm = data.reduce((arr, current, index) => {
    const pid = current.pid;
    const parent = treeMapList[pid];
    if (parent) {
      parent.child ? parent.child.push(current) : (parent.child = [current]);
    } else {
      arr.push(current);
    }
    return arr;
  }, []);
  return mm;
}

/**
 * 6.
 * 递归求出最底层的内容
 * 这里需要使用到递归
 * 情形就是这个结构可能是无限层的训话，你也不知道
 * 它到底有多少层，怎么办，只能使用递归
 * 注意这里也有两个限制条件就是第一它的子数据的属性名是children
 * 然后需要的目标值是name,
 * 还有就是这是一个函数套函数的形式，为了给他传参，这里为了把val传递个
 * 真正的递归函数，直接在函数的内部调用了digital函数，这个写法后面要记住
 */
const diguifunction = (val) => {
  let list = val;
  var datas = [];
  const getStr = function (list) {
    list.forEach((item) => {
      if (item.children) {
        getStr(item.children);
      } else {
        datas.push(item.name);
      }
    });
  };
  getStr(list);
  return datas;
};

/**
 * 6.2 递归处理数据
 *
 * @export
 * @param {全国省市区的地理列表} arr
 * @return {排除区域的省市列表}
 */
function digui(arr) {
  let brr = [];
  arr.map((item) => {
    let shdow = { ...item };
    let tmp = {};
    // 这个队规需要放在数据处理的上面，让他先判断是否需要递归
    // 然后再进行数据的处理
    if (shdow.areaList && shdow.level !== 1) {
      shdow.areaList = digui(shdow.areaList);
    }
    if (shdow.level === 0) {
      tmp = { ...shdow };
      brr.push(tmp);
    } else if (shdow.level === 1) {
      // 不能整体复制，因为它的areaList即便是一个空数组
      // 组件还是会展示这个空数组的选项，只不过值为空
      tmp.code = shdow.code;
      tmp.level = shdow.level;
      tmp.name = shdow.name;
      brr.push(tmp);
    }
  });
  return brr;
}

/**
 * 7.表格增删改查使用
 * 快速复制目标数组中的一部分属性到自己的数组
 * 不存在的key值不复制
 * 注意是obj2复制obj的值
 */
function objCoppy(obj1, obj2) {
  Object.keys(obj2).forEach((key) => {
    obj2[key] = obj1[key];
  });
}

/**
 * 8.
 * 截取字符串，并且插入指定的符号
 * 经常用于日期的显示的处理
 * 比如截取日期的后4位，并且添加-
 * 仅限于截取字符串的后四位，后面的symbol是自己需要添加的符号
 * arr是一个数组类型的数据，使用在三问四查导出中日期的处理
 * 这个可以自定义
 */
function jiequAdd(arr, symbol) {
  let brr = [];
  let crr = [];
  arr.map((item) => {
    let s = item.substring(item.length - 4);
    brr.push(s);
  });
  brr.map((val) => {
    let asd = val.split("");
    asd.splice(2, 0, symbol);
    bsd = asd.join("");
    crr.push(bsd);
  });
  return crr;
}

/**
 * 9
 *测试使用document this
 *用法和多行注释的写法一致
 * 反转字符串的方法，
 * 参考了反转数组的方法，进行了一个封装
 * str是传入的一个字符串
 * @param {*} str 自己想要反转的字符串
 */
const reverse = (str) => str.split("").reverse().join("");

/**
 * 10
 * 这里指的是数组中套对象的方式
 * 用来进行数组自身的去重，判断依据是数组的某一个属性值
 * 传入的是它的属性名
 * 适用于获取了用户的信息集合，用来做信息的去重操作
 * 比如按用户名或者是用户编号，其中某一个确定的不重复的数据来删除
 * 这样处理之后，会保留下按照这个参数出现的第一个元素，之后的会被删掉
 * @param {*} arr目标数组，内部可能有重复的元素
 * @param {*} keyName 去重的依据，就是对象名对应的值，
 * 比如根据id或name来判断
 * @return {*} 去重之后的新的数组
 */
function quchong(arr, keyName) {
  let newobj = {};
  let am = arr.reduce((preVal, curVal) => {
    // 这里的代码的意思是如果有，就什么也不做
    // 就证明之前不存在这个值，那就是不曾重复的数据，就塞到新的数组中去
    // (newobj[curVal[keyName]] = preVal.push(curVal))这行代码的解释
    // 就是给newobj的[curVal[keyName]]属性赋予一个新的值，这个值是一个表达式的返回值
    // 这个表达式就是preVal.push(curVal)，往已有数据中添加新值的返回值
    // 就是新的数组的长度
    newobj[curVal[keyName]]
      ? ""
      : (newobj[curVal[keyName]] = preVal.push(curVal));
    return preVal;
  }, []);
  return am;
}

/**
 * 11
 * 简单的数组去重
 * 参数可以是一个数组也可以是字符串
 * 返回值是一个数组形式
 * @param {*} arr 目标值
 * @returns
 */
function jiandanquchong(arr) {
  var newArry = [];
  for (let i = 0; i < arr.length; i++) {
    if (newArry.indexOf(arr[i]) === -1) {
      newArry.push(arr[i]);
    }
  }
  return newArry;
}

/**
 * 12
 * 数组2根据数组1做判断，去除数组1中已有的元素
 * 处理的结果就是最后剩的数据都是数组2中独有的数据，跟数据1没有重复
 * 这里添加的也是数组套对象的格式
 * 适用于固定，非固定人员的去重
 * 这个函数可以用于数据清洗或数据预处理，
 * 从两个数组中找出并返回那些只存在于第二个数组但第一个数组中没有的元素
 * @param {*} arr1 用来做参考的数组，不能修改
 * @param {*} arr2 根据数组1来删除自己与数组1重复的内容
 * @param {*} keyname 用来做判定的唯一标识，是以个字符串类型
 * @return {*} 去重之后的新的数组
 */
function shuzuClean(arr1, arr2, keyname) {
  let sumName = [];
  arr1.map((item) => {
    sumName.push(item[keyname]);
  });
  // console.log(sumName)
  arr3 = arr2.filter((item) => {
    // 保留arr2中和arr1相同的部分
    // return sumName.includes(item[keyname])
    // 保留arr2中和arr1不同的部分
    return !sumName.includes(item[keyname]);
  });
  return arr3;
}

/**
 * 优化后的方法，运行效率更佳
 * 主要是使用了Set这个数据结构，set类型的数据，查找的效率要明显的高于Array
 * 然后使用了一次filter函数，筛选出arr2数组中不存在于arr1的元素
 * @param {*} arr1
 * @param {*} arr2
 * @param {*} keyname
 * @returns
 */
function shuzuClean2(arr1, arr2, keyname) {
  let sumName = new Set(arr1.map((item) => item[keyname]));
  return arr2.filter((item) => {
    return !sumName.has(item[keyname]);
  });
}

/**
 * 13
 * 创建一个函数，用来判断两个简单类型的数组【1,3,5】
 * 他们之间的数据有没有重复
 * 如果有重复，就返回false
 * 如果没有重复，就返回true
 */
function isRepeat(arr, brr) {
  let crr = arr.concat(brr);
  let drr = [...new Set(crr)];
  if (crr.length === drr.length) {
    return true;
  } else {
    return false;
  }
}

/**
 * 这个是更为简化的版本
 * @param {*} arr
 * @param {*} brr
 * @returns
 */
function isRepeat2(arr, brr) {
  return new Set(arr.concat(brr)).size === arr.length + brr.length;
}

/**
 * 14
 * 用来判断一个数组是否是空数组，
 * 前提是这个变量必须存在，如果不存在，是不能判断的
 * 简化的写法是这种形式
 * if (item.goods && item.goods.length > 0)
 * @param {*} arr 需要判定的一个目标数组
 * @returns 返回布尔值true或者false
 */
function isArrarExact(arr) {
  // if (!arr) {
  //   return false
  // } else {
  //   if (arr.length > 0) {
  //     return true
  //   }
  //   return false
  // }
  // 新的简化写法也可以实现
  if (arr && arr.length > 0) {
    return true;
  } else {
    return false;
  }
}

/**
 * 15
 * 判断一个对象是否是空对象
 * @param {*} object1
 * @param {*} object2
 * @returns
 */
function isObjectExact(obj) {
  if (Object.keys(obj).length === 0) {
    return false;
  } else {
    return true;
  }
}

/**
 * 16
 * 点击事件之后，判断一个值是删除还是添加
 * 一般用于多选元素是使用，如果没选就加上
 * 如果已经选过了就删除
 * @param {*} arr 选择的元素的id或者下标的集合，是一个数组
 * @param {*} a 点击的元素的表标识，可以是id也可是name
 * 两个参数类型一致就可以
 */
function switchState(arr, a) {
  if (arr.includes(a)) {
    let inx = arr.indexOf(a);
    // 然后删除它
    arr.splice(inx, 1);
  } else {
    arr.push(a);
  }
}

/**
 * 17
 * 判断两个对象是否完全相等，使用的是最原始的循环的方法
 * 这个运行速度是最快地
 * 对比es6的新方法Object.entries(object1).toString() === Object.entries(object2).toString()
 * 虽然方法复杂，但是运行速度更快
 *  @param {*} object1 目标对象1
 * @param {*} object2 目标对象 2
 * @return {*} 用于判断是否相等的布尔值
 */
function ifCompare(object1, object2) {
  var o1keys = Object.keys(object1);
  var o2keys = Object.keys(object2);
  // 首先比较属性的个数，如果个数都不一致，那肯定相等
  if (o2keys.length !== o1keys.length) return false;
  for (let i = 0; i <= o1keys.length - 1; i++) {
    let key = o1keys[i];
    // 如果第二个对象的键名不包含第一个的，肯定更不相等
    if (!o2keys.includes(key)) return false;
    // 如果包含，但是第二个的键名对应的值和第一个的不相等，也不相等
    if (object2[key] !== object1[key]) return false;
  }
  return true;
}

/**
 * 18
 * 求数组的最大值
 * @param {*} arr 纯数字组成的数组
 * @returns
 */
function getMax(arr) {
  var max = arr[0];
  for (i = 0; i < arr.length; i++) {
    max = arr[i] > max ? arr[i] : max;
  }
  return max;
}

/**
 * 19
 * 获取数组的最小值
 * @param {*} arr
 * @returns
 */
function getMin(arr) {
  var min = arr[0];
  for (let i = 0; i < arr.length; i++) {
    min = arr[i] < min ? arr[i] : min;
  }
  return min;
}

/**
 * 20
 * 数组的求和
 * @param {*} arr
 * @returns
 */
function getSum(arr) {
  return arr.reduce(function (sum, value, index) {
    sum = sum + value;
    return sum;
  }, 0);
}

/**
 * 21
 * 判断一个数据是否在给定的数值范围之内
 * 适用于日期的简单判断，或者数值的判断
 * @param {*} arr 对比的参照数组
 * @param {*} b 需要比较的目标值
 * @return {true或者false}
 */
function CompareDate(arr, b) {
  let max = Math.max.apply(this, arr);
  let min = Math.min.apply(this, arr);
  // console.log(min, max)
  if (b < min || b > max) {
    return false;
  } else {
    return true;
  }
}

/**
 * 22
 * 用来颜色的分级
 * 需要的是#fff类型的颜色数据
 * @param {*} startColor开始的颜色
 * @param {*} endColor结束的颜色
 * @param {*} step一共分几个层次
 * @return {*}
 */
function ChangeColorStep(startColor, endColor, step) {
  function gradientColor(startColor, endColor, step) {
    startRGB = this.colorRgb(startColor); //转换为rgb数组模式
    startR = startRGB[0];
    startG = startRGB[1];
    startB = startRGB[2];
    endRGB = this.colorRgb(endColor);
    endR = endRGB[0];
    endG = endRGB[1];
    endB = endRGB[2];
    sR = (endR - startR) / step; //总差值
    sG = (endG - startG) / step;
    sB = (endB - startB) / step;
    var colorArr = [];
    for (var i = 0; i < step; i++) {
      //计算每一步的hex值
      var hex = this.colorHex(
        "rgb(" +
          parseInt(sR * i + startR) +
          "," +
          parseInt(sG * i + startG) +
          "," +
          parseInt(sB * i + startB) +
          ")"
      );
      colorArr.push(hex);
    }
    return colorArr;
  }
  // 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
  gradientColor.prototype.colorRgb = function (sColor) {
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    var sColor = sColor.toLowerCase();
    if (sColor && reg.test(sColor)) {
      if (sColor.length === 4) {
        var sColorNew = "#";
        for (var i = 1; i < 4; i += 1) {
          sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
        }
        sColor = sColorNew;
      }
      //处理六位的颜色值
      var sColorChange = [];
      for (var i = 1; i < 7; i += 2) {
        sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2)));
      }
      return sColorChange;
    } else {
      return sColor;
    }
  };
  // 将rgb表示方式转换为hex表示方式
  gradientColor.prototype.colorHex = function (rgb) {
    var _this = rgb;
    var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
    if (/^(rgb|RGB)/.test(_this)) {
      var aColor = _this.replace(/(?:(|)|rgb|RGB)*/g, "").split(",");
      var strHex = "#";
      for (var i = 0; i < aColor.length; i++) {
        var hex = Number(aColor[i]).toString(16);
        hex = hex < 10 ? 0 + "" + hex : hex; // 保证每个rgb的值为2位
        if (hex === "0") {
          hex += hex;
        }
        strHex += hex;
      }
      if (strHex.length !== 7) {
        strHex = _this;
      }
      return strHex;
    } else if (reg.test(_this)) {
      var aNum = _this.replace(/#/, "").split("");
      if (aNum.length === 6) {
        return _this;
      } else if (aNum.length === 3) {
        var numHex = "#";
        for (var i = 0; i < aNum.length; i += 1) {
          numHex += aNum[i] + aNum[i];
        }
        return numHex;
      }
    } else {
      return _this;
    }
  };
  return new gradientColor(startColor, endColor, step);
}

/**
 * 23 非空判断
 * let selectLeaveByDate = {
  errCode: 0,
  msg: 'success',
  dataList: [
    {
    // 判断是否有外出权限
    fCreateId: '11233',
    fCreateId: '',
    }
  ],
  dataList2: null,
  data: null,
  page: 0,
  totalPage: 0,
  totalSize: 0,
  timestamp: null,
  yytoken: null,
  loginId: null
}
上面是判断的数据的原始类型，这里提供一个案例，
后续需要判断的数据的类型和字段，需要自己填入
 * @returns 返回值为是否为空的结果true和false
 */
function JudgeEmpty() {
  let amd;
  if (selectLeaveByDate.dataList) {
    if (Object.keys(selectLeaveByDate.dataList).length == 0) {
      amd = "数组没有值";
    } else {
      if (selectLeaveByDate.dataList[0].fCreateId) {
        amd = true;
      } else {
        amd = false;
      }
    }
  } else {
    amd = "没有返回数组";
  }
  return amd;
}

// 可选链的写法,可选链操作符?. 问号加个点 两者的效果一样
// 有了可选链，就可以对一个为null或者undefined属性安全引用：
// node 环境内不支持，需要进行修改才可以支持这个语法
// 参考：https://blog.csdn.net/weixin_40579884/article/details/115670580?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_default&utm_relevant_index=1
// function JudgeEmpty2 () {
//   let m = selectLeaveByDate?.dataList?.[0]?.fCreateId
//   if (m) {
//     return true
//   } else {
//     return false
//   }
// }

/**
 * 24.深复制
 * 这里可以复制任意的复杂类型
 * @param {*} obj 需要复制的目标
 * @return {*}  反回的新数据
 */
function deepClone(obj) {
  let res = null;
  const reference = [Date, RegExp, Set, WeakSet, Map, WeakMap, Error];
  // node 环境中不支持可选链操作符 ?. 但是浏览器环境支持
  // if (reference.includes(obj?.constructor)) {
  if (reference.includes(obj.constructor)) {
    res = new obj.constructor(obj);
  } else if (Array.isArray(obj)) {
    res = [];
    obj.forEach((e, i) => {
      res[i] = deepClone(e);
    });
  } else if (typeof obj === "Object" && obj !== null) {
    res = {};
    for (const key in obj) {
      if (Object.hasOwnProperty.call(obj, key)) {
        res[key] = deepClone(obj[key]);
      }
    }
  } else {
    res = obj;
  }
  return res;
}

/**
 * 25.字符串数组转数字数组
 * @param {*} nums 目标数组是一个字符串格式的
 * @returns 返回值是一根数字格式的数组
 * 可以使用简化方法 nums.map(Number)
 */
function toNumber(nums) {
  // for (let i = 0; i < nums.length; i++) {
  //   nums[i] = parseInt(nums[i])
  // }
  // return nums

  // 简化写法
  let arr = nums.map(Number);
  return arr;
}

/**
 * 26.截取地址中的参数
 *
 * @export
 * @param {*} url window.location.href   地址
 *  http://localhost:8080/#/progressStatistics?a=1&b=2
 * @param {*} variable a 参数的属性名
 * @return {*} 1 对应a的值
 */
function getQueryVariable(key, str = window.location.href) {
  const queryStart = str.indexOf("?");
  if (queryStart === -1) {
    return null;
  }
  // 对对应标准的数据传输格式 http://localhost:8080/#/home?date=2024-04-08
  // const queryStr = str.slice(queryStart + 1);
  // 对应不太标准的格式，不确定参数拼接在哪里 http://localhost:8080/?date=2024-04-08?#/home
  // 比如这种，有可能在地址中间这种
  const queryStr = str.split("?")[1];
  const params = {};
  const pairs = queryStr.split("&");
  for (const pair of pairs) {
    const [name, value] = pair.split("=");
    params[decodeURIComponent(name)] = decodeURIComponent(value || "");
  }
  return key ? params[key] : params;
}

/**
 * 27.截取公共地址
 * 参数就当前路径
 * @export http://localhost:8080/#/progressStatistics?a=1&b=2
 * @return {当前项目的公共路径}  http://localhost:8080/
 */
function getUrl() {
  const url = location.href;
  const _index = url.indexOf("#");
  if (_index !== -1) {
    return url.substring(0, _index);
  }
  return url;
}

// 28.获取视口高度
function getclientWidth() {
  const clientWidth =
    window.innerWidth ||
    document.documentElement.clientWidth ||
    document.body.clientWidth;
  return clientWidth;
}

// 29.获取视口高度
function getclientHeight() {
  const clientHeight =
    window.innerHeight ||
    document.documentElement.clientHeight ||
    document.body.clientHeight;
  return clientHeight;
}
/**
 * 30.检测设备型号
 * navigator.userAgent当前用户的设备信息
 * Electron设备信息中独有的关键字，可以自己查询
 * 目前设备中独有的关键字是哪些，目前企信桌面端
 * 独有的就是这个Electron，可以使用它来做区分
 * @return {布尔值}
 */
function testEquipment() {
  let s = /(Electron)/i.test(navigator.userAgent);
  return s;
}

/**
 * 31.从数组中删除指定的元素
 * @param {*} arr 目标数组
 * @param {*} item 需要删除的元素
 * @returns
 */
// function remove (arr: any[], item: string) {
function remove(arr, item) {
  for (var i = arr.length - 1; i >= 0; i--) {
    if (arr[i] == item) {
      arr.splice(i, 1);
    }
  }
  return arr;
}

/**
 *31.1 从数组中删除数据
 *
 * @param {*} arr
 * @param {*} item
 * @return {*}
 */
function remove2(arr, item) {
  return arr.filter((item2) => {
    return item2 !== item;
  });
}

/**
 * 32判断一个值是否是无效值
 *
 * @param {*} variable
 * @return {*}
 */
function checkValueExistence(variable) {
  return (
    typeof variable !== "undefined" &&
    variable !== null &&
    [variable].every((value) => {
      return typeof value === "string" ? value.trim().length > 0 : true;
    })
  );
}

/**
 * 33.js的精确计算，防止出现 0.1 + 0.2 = 0.300000000004的情况
 * @param {*} arg1
 * @param {*} arg2
 * @returns
 */
function accAdd(arg1, arg2) {
  var r1, r2, m;
  try {
    r1 = arg1.toString().split(".")[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = arg2.toString().split(".")[1].length;
  } catch (e) {
    r2 = 0;
  }
  m = Math.pow(10, Math.max(r1, r2));
  return (parseInt(arg1 * m, 10) + parseInt(arg2 * m, 10)) / m;
}

/**
 * 根据第二个参数，删除原数组中的数据
 * 这个删除时直接删除，没有采用返回值的形式，直接是操作的原数组
 * 好处是可以直接在代码中调用这个函数，达到目的，省去了赋值的流程
 * 采用filter函数的方式会更简单一些
 * @param {*} arr 原数组
 * @param {*} brr 指导删除的参数
 */
function deleteSth(arr, brr) {
  arr.forEach((item, index, arr) => {
    if (brr.includes(item.name)) {
      arr.splice(index, 1);
    }
  });
}

/**
 *
 * @param {*} arr 原数组
 * @param {*} brr 指导删除的数组
 * @returns
 */
function deleteSth2(arr, brr) {
  return arr.filter((item) => {
    return !brr.includes(item.name);
  });
}

/**
 *
 * @param {*} number
 * @returns 每隔三位，添加一个空格
 */
function formatNumberWithSpaces(nums) {
  let num = (nums || 0).toString();
  let result = "";
  while (num.length > 3) {
    result = " " + num.slice(-3) + result;
    num = num.slice(0, num.length - 3);
  }
  if (num) {
    result = num + result;
  }
  return result;
}

module.exports = {
  isTypeOf, // 1.检测数据的类型
  changeTypeToArray, // 2.把对象序列化成数组格式的键值对对象
  changeTypeToObject, // 3.对应的数组反序列化
  cleanObjectNullValue, // 4.过滤对象中的空值，把值为空的属性去掉
  setTree, // 5.1.构建树形结构
  setTree2, // 5.2.构建树形结构
  diguifunction, // 6.递归求出最底层的内容
  digui, // 6.2 递归处理省市
  objCoppy, // 7.快速复制目标数组中的一部分属性到自己的数组,数据回显用
  jiequAdd, // 8.截取字符串，并且插入指定的符号
  reverse, // 9.反转字符串的方法，
  quchong, // 10.数组中套对象的格式的去重
  jiandanquchong, // 11.简单的数组去重
  shuzuClean, // 12.数组2根据数组1做判断，去除数组1中已有的元素
  shuzuClean2,
  isRepeat, // 13.判断两个简单类型的数组【1,3,5】有没有重复
  isRepeat2, // 13.2.判断两个简单类型的数组【1,3,5】有没有重复
  isArrarExact, // 14.用来判断一个数组是否是空数组
  isObjectExact, // 15.判断一个对象是否是空对象
  switchState, // 16.点击事件之后，判断一个值是删除还是添加
  ifCompare, // 17.判断两个对象是否完全相等
  getMax, // 18.求数组的最大值
  getMin, // 19.获取数组的最小值
  getSum, // 20.数组的求和
  CompareDate, // 21.判断一个数据是否在给定的数值范围之内
  ChangeColorStep, // 22.用来颜色的分级
  JudgeEmpty, // 23. 非空判断
  // JudgeEmpty2,
  deepClone, // 24.这里可以复制任意的复杂类型
  toNumber, // 25.字符串数组转数字数组
  getQueryVariable, // 26.截取url地址中的参数
  getUrl, // 27.截取路径
  getclientWidth, // 28.获取视口高度
  getclientHeight, // 29.获取视口高度
  testEquipment, // 30.检测设备型号
  remove, // 31.从数组中删除指定的元素
  remove2, // 31.1.从数组中删除指定的元素
  checkValueExistence, // 32.判断一个值是否是无效值
  accAdd, // 33.js的精确计算
  deleteSth, // 34.根据目标直接做删除，操作原数组
  deleteSth2, // 35.根据目标直接做删除，操作原数组
  formatNumberWithSpaces, //给数字每隔三位添加一个空格
};
